package cn.com.blueInfo.config;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 自定义枚举处理器，处理没有@EnumValue注解的枚举
 * @param <T>
 */
public class CustomEnumTypeHandler<T extends Enum<T>> extends BaseTypeHandler<T> {

    private final Class<T> enumType;
    private final Method getCodeMethod; // 获取数据库存储值的方法（如getCode()）
    private final Method getByCodeMethod; // 反查枚举的静态方法（如getByCode()）

    /**
     * 构造器：传入枚举类型，通过反射获取约定的方法
     * @param enumType
     */
    public CustomEnumTypeHandler(Class<T> enumType) {
        this.enumType = enumType;

        try {
            // 假设枚举类有 getCode() 方法（返回值类型与数据库字段匹配）
            getCodeMethod = enumType.getMethod("getCode");
            // 假设枚举类有静态方法 getByCode(参数类型)
            // 参数类型与getCode()的返回值类型一致
            Class<?> returnType = getCodeMethod.getReturnType();
            getByCodeMethod = enumType.getMethod("getByCode", returnType);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("枚举类" + enumType.getName() + "未实现约定的方法（getCode() 或 getByCode()）", e);
        }

    }

    /**
     * 插入/更新时：枚举 → 数据库值
     * @param ps
     * @param i
     * @param parameter
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        try {
            // 调用枚举的 getCode() 方法获取存储值
            Object code = getCodeMethod.invoke(parameter);
            // 根据值的类型设置到PreparedStatement（这里以int为例，可扩展其他类型）
            if (code instanceof Integer) {
                ps.setInt(i, (Integer) code);
            } else if (code instanceof String) {
                ps.setString(i, (String) code);
            }
            // 可继续扩展long、short等类型
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException("枚举转换为数据库值失败", e);
        }
    }

    /**
     * 查询时：数据库值 → 枚举（根据列名）
     * @param rs
     * @param columnName
     * @return
     * @throws SQLException
     */
    @Override
    @SuppressWarnings("unchecked") // 抑制未检查的转换警告
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        try {
            // 获取数据库字段类型（与getCode()返回值一致）
            Class<?> codeType = getCodeMethod.getReturnType();
            Object code;
            if (codeType == Integer.class) {
                code = rs.getInt(columnName);
            } else if (codeType == String.class) {
                code = rs.getString(columnName);
            } else {
                throw new RuntimeException("不支持的枚举code类型：" + codeType);
            }
            // 调用静态方法 getByCode(code) 反查枚举
            return (T) getByCodeMethod.invoke(null, code);
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException("数据库值转换为枚举失败", e);
        }
    }

    /**
     * 查询时：数据库值 → 枚举（根据列索引）
     * @param rs
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    @Override
    @SuppressWarnings("unchecked") // 抑制未检查的转换警告
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        try {
            Class<?> codeType = getCodeMethod.getReturnType();
            Object code;
            if (codeType == Integer.class) {
                code = rs.getInt(columnIndex);
            } else if (codeType == String.class) {
                code = rs.getString(columnIndex);
            } else {
                throw new RuntimeException("不支持的枚举code类型：" + codeType);
            }
            return (T) getByCodeMethod.invoke(null, code);
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException("数据库值转换为枚举失败", e);
        }
    }

    @Override
    @SuppressWarnings("unchecked") // 抑制未检查的转换警告
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        try {
            Class<?> codeType = getCodeMethod.getReturnType();
            Object code;
            if (codeType == Integer.class) {
                code = cs.getInt(columnIndex);
            } else if (codeType == String.class) {
                code = cs.getString(columnIndex);
            } else {
                throw new RuntimeException("不支持的枚举code类型：" + codeType);
            }
            return (T) getByCodeMethod.invoke(null, code);
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException("数据库值转换为枚举失败", e);
        }
    }
}
